//
//  Calendar.swift
//  Documentation
//
//  Created by Toj on 11/13/23.
//

/**
 `Calendar` 封装了有关计算时间系统的信息, 其中定义了一年的开始、长度和划分.
 它提供了关于日历的信息和对日历计算的支持,
 例如: 确定给定日历单位的范围和向给定的绝对时间添加单位.
*/
public struct Calendar : Hashable, Equatable, ReferenceConvertible, Sendable {

    public typealias ReferenceType = NSCalendar

    /// 返回用户当前的日历
    public static var current: Calendar { get }

    /// 一个日历跟踪用户的首选日历的变化
    /// 如果发生变异, 这个日历将不再跟踪用户的首选日历.
    /// - NOTE: 自动更新日历只会与另一个自动更新日历进行相等比较.
    public static var autoupdatingCurrent: Calendar { get }

    /// 根据日历标识符初始化
    ///
    /// - parameter identifier: 日历类型标识符
    public init(identifier: Calendar.Identifier)

    /// 日历类型标识符
    public var identifier: Calendar.Identifier { get }

    /// 日历的区域化
    public var locale: Locale? = nil

    /// 日历时区, 默认Asia/Shanghai
    public var timeZone: TimeZone

    /// 日历的第一个工作日
    public var firstWeekday: Int = 1 // 星期一

    /// 第一周的最少天数
    public var minimumDaysInFirstWeek: Int

    /// 纪元标志符
    /// locale: en_US, 默认为 ["BCE", "CE"]
    /// locale: zh_CN, 默认为 ["公元前", "公元"]
    public var eraSymbols: [String] { get }
    
    /// 纪元标志符
    /// locale: en_US时, 默认为 ["Before Christ", "Anno Domini"]
    /// locale: zh_CN时, 默认为 ["公元前", "公元"]
    public var longEraSymbols: [String] { get }

    /// 月份标志符
    public var monthSymbols: [String] { get }
    public var shortMonthSymbols: [String] { get }
    public var veryShortMonthSymbols: [String] { get }
    public var standaloneMonthSymbols: [String] { get }
    public var shortStandaloneMonthSymbols: [String] { get }
    public var veryShortStandaloneMonthSymbols: [String] { get }

    /// 星期标志符
    public var weekdaySymbols: [String] { get }
    public var shortWeekdaySymbols: [String] { get }
    public var veryShortWeekdaySymbols: [String] { get }
    public var standaloneWeekdaySymbols: [String] { get }
    public var shortStandaloneWeekdaySymbols: [String] { get }
    public var veryShortStandaloneWeekdaySymbols: [String] { get }

    /// 季度标志符
    public var quarterSymbols: [String] { get }
    public var shortQuarterSymbols: [String] { get }
    public var standaloneQuarterSymbols: [String] { get }
    public var shortStandaloneQuarterSymbols: [String] { get }

    /// 时间段标志符
    /// 上午
    public var amSymbol: String { return "AM" }
    /// 下午
    public var pmSymbol: String { return "PM" }

    /// 指定组件的最小范围限制,
    /// NOTE: day天数: 2月最少28天 [1, 28]
    ///
    public func minimumRange(of component: Calendar.Component) -> Range<Int>?

    /// 指定组件的最大范围限制
    /// NOTE: day天数: 最多是31天 [1, 31]
    public func maximumRange(of component: Calendar.Component) -> Range<Int>?

    /// 小组件可以在大组件中使用的范围
    public func range(of smaller: Calendar.Component, in larger: Calendar.Component, for date: Date) -> Range<Int>?

    /// 指定日期所处的开始日期与持续时间
    public func dateInterval(of component: Calendar.Component, start: inout Date, interval: inout TimeInterval, for date: Date) -> Bool

    /// 指定日期所处的日期范围
    @available(macOS 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *)
    public func dateInterval(of component: Calendar.Component, for date: Date) -> DateInterval?

    /// 指定日期的小组件位于大组件的排序位置
    public func ordinality(of smaller: Calendar.Component, in larger: Calendar.Component, for date: Date) -> Int?

    /// 返回一个新日期, 通过组件给指定日期增加时间组件内部值
    /// - parameter wrappingComponents: 为true时，当组件值超时最大限制时，会%最大值
    /// e.g.:
    /// 给秒增加61, 61%60=1 实际就增加1秒
    public func date(byAdding components: DateComponents, to date: Date, wrappingComponents: Bool = false) -> Date?

    /// 与上面雷同, 只不过参数不同
    @available(iOS 8.0, *)
    public func date(byAdding component: Calendar.Component, value: Int, to date: Date, wrappingComponents: Bool = false) -> Date?

    /// 根据components日期组件转换为日期
    public func date(from components: DateComponents) -> Date?

    /// 通过日历组件将日期转换为日期组件
    public func dateComponents(_ components: Set<Calendar.Component>, from date: Date) -> DateComponents

    /// 通过时区将日期转换为日期组件
    @available(iOS 8.0, *)
    public func dateComponents(in timeZone: TimeZone, from date: Date) -> DateComponents

    /// 获取两个日期的差, components想要的组件信息, 保存在时间组件内
    public func dateComponents(_ components: Set<Calendar.Component>, from start: Date, to end: Date) -> DateComponents

    /// 获取两个日期组件的差, components想要的组件信息, 保存在时间组件内
    @available(iOS 8.0, *)
    public func dateComponents(_ components: Set<Calendar.Component>, from start: DateComponents, to end: DateComponents) -> DateComponents

    /// 指定日期对应日历组件的值
    @available(iOS 8.0, *)
    public func component(_ component: Calendar.Component, from date: Date) -> Int

    /// 指定日期的第一个时刻
    @available(iOS 8.0, *)
    public func startOfDay(for date: Date) -> Date

    /// 比较两日期对应组件值的大小
    /// NOTE: 并不是单独比较组件的值. 实际上是将日期转换为秒, 再比较大小
    @available(iOS 8.0, *)
    public func compare(_ date1: Date, to date2: Date, toGranularity component: Calendar.Component) -> ComparisonResult

    /// 两日期对应组件值是否相同
    /// NOTE: 也是比较的秒
    @available(iOS 8.0, *)
    public func isDate(_ date1: Date, equalTo date2: Date, toGranularity component: Calendar.Component) -> Bool

    /// 两日期是否在同一天
    @available(iOS 8.0, *)
    public func isDate(_ date1: Date, inSameDayAs date2: Date) -> Bool

    /// 指定日期是否属于今天
    public func isDateInToday(_ date: Date) -> Bool

    /// 指定日期是否属于昨天
    @available(iOS 8.0, *)
    public func isDateInYesterday(_ date: Date) -> Bool

    /// 指定日期是否属于明天
    @available(iOS 8.0, *)
    public func isDateInTomorrow(_ date: Date) -> Bool

    /// 指定日期是否属于周末
    /// NOTE: 有些地区的周末并不是周六与周日
    @available(iOS 8.0, *)
    public func isDateInWeekend(_ date: Date) -> Bool

    /// 获取指定日期的周末开始日期与持续时间
    /// 若能获取到周末，该方法返回true
    @available(iOS 8.0, *)
    public func dateIntervalOfWeekend(containing date: Date, start: inout Date, interval: inout TimeInterval) -> Bool

    /// 获取指定日期的周末的范围
    @available(macOS 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *)
    public func dateIntervalOfWeekend(containing date: Date) -> DateInterval?

    /// 返回指定日期的下一个周末开始日期与持续时间
    @available(iOS 8.0, *)
    public func nextWeekend(startingAfter date: Date, start: inout Date, interval: inout TimeInterval, direction: Calendar.SearchDirection = .forward) -> Bool

    /// 获取指定日期的下一个周末的范围
    @available(macOS 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *)
    public func nextWeekend(startingAfter date: Date, direction: Calendar.SearchDirection = .forward) -> DateInterval?

    /// 匹配满足日期组件(或最接近)的日期
    /// 如果不可能完全匹配, 并且matchingPolicy为strict, 则会将nil传递给闭包, 并且枚举结束.
    /// 逻辑上, strict匹配将无限期地搜索到将来, 但如果找不到匹配项, 则继续进行枚举并没有意义.
    /// 若已成功匹配日期, 可通过在闭包中将inout Bool值设置为true来停止枚举并函数返回.
    /// block:
    /// - Date?为匹配到的日期
    /// - Bool为true时, 表示此日期为完全匹配日期组件.
    /// - inout Bool赋值为true后, 会停止遍历.
    @available(iOS 8.0, *)
    public func enumerateDates(
        startingAfter start: Date,
        matching components: DateComponents,
        matchingPolicy: Calendar.MatchingPolicy,
        repeatedTimePolicy: Calendar.RepeatedTimePolicy = .first,
        direction: Calendar.SearchDirection = .forward,
        using block: (_ result: Date?, _ exactMatch: Bool, _ stop: inout Bool) -> Void)

    /// 获取第一个满足日期组件(或最接近)的日期
    @available(iOS 8.0, *)
    public func nextDate(after date: Date, matching components: DateComponents, matchingPolicy: Calendar.MatchingPolicy, repeatedTimePolicy: Calendar.RepeatedTimePolicy = .first, direction: Calendar.SearchDirection = .forward) -> Date?

    // MARK: - 日期的更改
    
    /// 通过组件值更改日期
    @available(iOS 8.0, *)
    public func date(bySetting component: Calendar.Component, value: Int, of date: Date) -> Date?

    
    /// 通过时分秒更改日期
    @available(iOS 8.0, *)
    public func date(bySettingHour hour: Int, minute: Int, second: Int, of date: Date, matchingPolicy: Calendar.MatchingPolicy = .nextTime, repeatedTimePolicy: Calendar.RepeatedTimePolicy = .first, direction: Calendar.SearchDirection = .forward) -> Date?

    /// 日期是否匹配日期组件
    @available(iOS 8.0, *)
    public func date(_ date: Date, matchesComponents components: DateComponents) -> Bool
    
    public func hash(into hasher: inout Hasher)
    public static func == (lhs: Calendar, rhs: Calendar) -> Bool
    public var hashValue: Int { get }
}
